package com.recharge.web.controller;


import com.recharge.common.annotation.DecryptParam;
import com.recharge.common.enums.ErrorCode;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.web.ObjectResponse;
import com.recharge.common.util.Constant;
import com.recharge.common.util.DESUtil;
import com.recharge.common.util.RedisUtil;
import com.recharge.common.util.SnowflakeIdWorker;
import com.recharge.domain.vo.*;
import com.recharge.service.*;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api")
public class ApiController {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    private MerchantService merchantService;

    @Autowired
    private MerchantAccountService merchantAccountService;

    @Autowired
    private ProviderApiService providerApiService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private ProviderGoodsService providerGoodsService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RedisTemplate<String, Object> redisMessageTemplate;

    @Autowired
    private RechargeService rechargeService;

    @Value("${node.name}")
    private String nodeName;


    @RequestMapping(value = "/recharge.html", method = RequestMethod.POST)
    @ApiOperation(value = "充值接口", notes = "")
    @DecryptParam
    public @ResponseBody
    ObjectResponse recharge(@RequestBody EncryptionRechargeDO encryptionRecharge, HttpServletRequest request) {

        RechargeDO domain = encryptionRecharge.getRechargeDO();
        long total = 0L;
        ObjectResponse response = new ObjectResponse();
        ResultDO result = null;
        MerchantDO merchant = (MerchantDO) redisUtil.get(Constant.MERCHANT_INFO + ":" + domain.getMerchantNo());
        if (merchant == null) {
            logger.debug("redis 里面没有商户信息读取数据库的商户信息");
            //调用验证接口
            result = merchantService.getByMerchantNO(domain.getMerchantNo());
            if (!result.isSuccess()) {
                response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
                response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
                return response;
            }
            merchant = (MerchantDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
            if (merchant == null) {
                response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
                response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
                return response;
            }
            if (redisUtil.get(Constant.MERCHANT_INFO) == null) {
                redisUtil.set(Constant.MERCHANT_INFO + ":" + domain.getMerchantNo(), merchant);
            }
        }

         MerchantAccountDO merchantAccountDO = (MerchantAccountDO) redisUtil.get(Constant.MERCHANT_ACCOUNT + ":" + merchant.getId());
        if (merchantAccountDO == null) {
            result = merchantAccountService.get(merchant.getId());
            if (!result.isSuccess()) {
                response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
                response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
                return response;
            }
            merchantAccountDO = (MerchantAccountDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
            redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + merchantAccountDO.getId(), domain);
        }
        //logger.info("从Redis中加载商户账户判断余额,商户当前余额为：" + merchantAccountDO.getBalance() + " 固定授信金额: " + merchantAccountDO.getCredit() + ":" +domain.getPrice());
        if ((merchantAccountDO.getBalance() + merchantAccountDO.getCredit()) < domain.getPrice()) {
            response.setMsg(ErrorCode.API_USER_BALANCE_STATUS_ERROR.getMsg());
            response.setStatus(ErrorCode.API_USER_BALANCE_STATUS_ERROR.getCode());
            return response;
        }

        String bindAddress = merchant.getBindAddress();
        //logger.debug("当前绑定的IP地址为：" + bindAddress);
        if (StringUtils.isNotEmpty(bindAddress)) {
            //logger.debug("验证商户绑定的IP地址或者域名");

            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            logger.debug("请求IP地址：" + ip);
            if (!bindAddress.equals(ip)) {
                logger.debug("绑定地址错误");
                response.setMsg(ErrorCode.API_INVALID_REQUEST_ADDRESS_ERROR.getMsg());
                response.setStatus(ErrorCode.API_INVALID_REQUEST_ADDRESS_ERROR.getCode());
                return response;
            }
        }

        //logger.debug("验证商户合法性" + merchant);
        if (merchant.getPrivateKey().equals(encryptionRecharge.getPrivateKey())) {
            //logger.debug("验证用户的KEY");

            String merchantInfo = DESUtil.decrypt(merchant.getPassword(), encryptionRecharge.getPrivateKey());

            String merchantNo = merchantInfo.split("-")[0];
            String password = merchantInfo.split("-")[1];
            //logger.debug("验证用户 merchantNo：" + merchantNo + ": password:" + password);
            if (merchant.getPassword().equals(password) && merchant.getMerchantNo().equals(merchantNo)) {
                domain.setMerchantId(merchant.getId());
                domain.setMerchantName(merchant.getMerchantName());
                domain.setCallbackUrl(merchant.getCallback());
                domain.setOrderNo(SnowflakeIdWorker.getNo());
                domain.setTransactionNo(SnowflakeIdWorker.getNo());
                domain.setNodeName(nodeName);
                domain.setRetryStatus(Constant.ORDER_RETRY_STATUS_0);
                //logger.warn("下游请求 " + domain);
                result = rechargeService.getHandlerOrderSize();
                Integer count = (int) result.getModel(ResultDO.FIRST_MODEL_KEY);
                if(count < 50000) {
                    redisMessageTemplate.convertAndSend("queue:" + nodeName, domain);
                } else {
                    response.setMsg(ErrorCode.API_SERVER_REQUEST_FULL.getMsg());
                    response.setStatus(ErrorCode.API_SERVER_REQUEST_FULL.getCode());
                    return response;
                }

            } else {
                response.setMsg(ErrorCode.API_MERCHANT_AUTHEN_ERROR.getMsg());
                response.setStatus(ErrorCode.API_MERCHANT_AUTHEN_ERROR.getCode());
                return response;
            }
        } else {
            response.setMsg(ErrorCode.API_MERCHANT_AUTHEN_ERROR.getMsg());
            response.setStatus(ErrorCode.API_MERCHANT_AUTHEN_ERROR.getCode());
            return response;
        }

        return response;
    }

    @RequestMapping(value = "/find_balance.html", method = RequestMethod.POST)
    @ApiOperation(value = "查询余额", notes = "")
    public @ResponseBody
    ObjectResponse<MerchantBalanceDO> findBalance(@RequestBody BalanceFindDO domain) {

        ObjectResponse<MerchantBalanceDO> response = new ObjectResponse();

        long currentTime = System.currentTimeMillis();
        Long lastTime = (Long) redisUtil.get(domain.getMerchantNo() + ":" + "find_balance");

        if (lastTime == null) {
            lastTime = 0L;
        }

        if (currentTime - lastTime < 8000L) {
            response.setMsg(ErrorCode.API_REQUEST_TIME_ERROR.getMsg());
            response.setStatus(ErrorCode.API_REQUEST_TIME_ERROR.getCode());
            return response;
        }

        ResultDO result = merchantService.getByMerchantNO(domain.getMerchantNo());
        if (!result.isSuccess()) {
            response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
            response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
            return response;
        }
        MerchantDO merchant = (MerchantDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
        result = merchantAccountService.get(merchant.getId());
        if (!result.isSuccess()) {
            response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
            response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
            return response;
        }
        MerchantAccountDO merchantAccountDO = (MerchantAccountDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
        MerchantBalanceDO data = new MerchantBalanceDO();
        Long balance = merchantAccountDO.getBalance();
        double value = 0.000;
        if (balance == null) {
            balance = 0L;
        } else {
            value = balance * 0.001;
        }

        data.setBalance(new BigDecimal(value).setScale(3, RoundingMode.DOWN).toString());

        data.setMerchantNo(merchant.getMerchantNo());
        response.setData(data);
        redisUtil.set(domain.getMerchantNo() + ":" + "find_balance", currentTime);
        return response;
    }

    @RequestMapping(value = "/find_order.html", method = RequestMethod.POST)
    @ApiOperation(value = "查询订单", notes = "")
    public @ResponseBody
    ObjectResponse<MerchantBalanceDO> findOrder(@RequestBody OrderFindDO domain) {

        ObjectResponse response = new ObjectResponse();

        long currentTime = System.currentTimeMillis();
        Long lastTime = (Long) redisUtil.get(domain.getMerchantNo() + ":" + "find_order");

        if (lastTime == null) {
            lastTime = 0L;
        }

        if (currentTime - lastTime < 8000L) {
            response.setMsg(ErrorCode.API_REQUEST_TIME_ERROR.getMsg());
            response.setStatus(ErrorCode.API_REQUEST_TIME_ERROR.getCode());
            return response;
        }

        //调用验证接口
        ResultDO result = merchantService.getByMerchantNO(domain.getMerchantNo());
        if (!result.isSuccess()) {
            response.setMsg(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getMsg());
            response.setStatus(ErrorCode.API_MERCHANT_NOT_EXITE_ERROR.getCode());
            return response;
        }

        result = orderService.getByMerchantSerialNumber(domain.getSerialNumber());

        if (!result.isSuccess()) {
            response.setMsg(result.getErrorCode().getMsg());
            response.setStatus(result.getErrorCode().getCode());
        } else {
            String status = "";
            OrderDO orderDO = (OrderDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
            if(orderDO == null){
                response.setMsg(ErrorCode.API_ORDER_NOT_EXIT_ERROR.getMsg());
                response.setStatus(ErrorCode.API_ORDER_NOT_EXIT_ERROR.getCode());
                return response;
            }
            if (orderDO.getStatus() == Constant.RECHARGE_ORDER_STATUS_3) {
                status = "success";
                response.setMsg(orderDO.getVoucherNo());
            } else if (orderDO.getStatus() == Constant.RECHARGE_ORDER_STATUS_5) {
                status = "process";
            }else if (orderDO.getStatus() == Constant.RECHARGE_ORDER_STATUS_4) {
                status = "fail";
            } else if (orderDO.getStatus() == Constant.RECHARGE_ORDER_STATUS_6) {
                status = "exception";
            } else if (orderDO.getStatus() == Constant.RECHARGE_ORDER_STATUS_7) {
                status = "unconfirmed";
            }
            response.setData(status);
        }
        redisUtil.set(domain.getMerchantNo() + ":" + "find_order", currentTime);
        return response;
    }

    @RequestMapping(value = "/callBack/{id}.html")
    @ApiOperation(value = "异步回调", notes = "")
    public String callBackTest(@PathVariable("id") String provideNo, HttpServletRequest request) {

        //接受param参数
        String paramStr = "";
        Map<String, String[]> parameterMap = request.getParameterMap();
        try {
            paramStr = parameterMap.entrySet()
                    .stream()
                    .map(entry -> String.format("\"%s\":\"%s\"", entry.getKey(), entry.getValue()[0]))
                    .collect(Collectors.joining(", "));
        } catch (Exception e) {
            logger.debug("接受参数异常");
        }

        String bodyStr = "";
        // 接收json或者xml数据
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
            String buffer = "";
            StringBuilder sb = new StringBuilder();
            while ((buffer = br.readLine()) != null) {
                sb.append(buffer);
            }
            bodyStr = sb.toString();
        } catch (IOException e) {
            logger.debug(e.getMessage());
        }
        paramStr = String.format("{%s}", paramStr);
        logger.warn(String.format("异步返回信息: \nparam:%s\nbodyParam:%s ", paramStr, bodyStr));

        ResultDO result = providerApiService.backOrder(provideNo, paramStr , bodyStr);

        return result.getModel(ResultDO.FIRST_MODEL_KEY).toString();
    }


    @RequestMapping(value = "/appendCard.html", method = RequestMethod.POST)
    @ApiOperation(value = "卡密充值卡添加接口", notes = "")
    public @ResponseBody
    ObjectResponse appendCard(@RequestBody EncryptionCardDO encryptionCardDO, HttpServletRequest request) {

        ObjectResponse response = new ObjectResponse();
        logger.info("============收到卡密添加请求============" + encryptionCardDO.getCardDO());

        ResultDO resultDO = providerGoodsService.provideGoods(encryptionCardDO.getCardDO(), Constant.PROVIDER_CARD_METHOD_API);
        if (!resultDO.isSuccess()) {
            response.setStatus(resultDO.getErrorCode().getCode());
            response.setMsg(resultDO.getErrorCode().getMsg());
        }
        logger.debug("请求卡密添加接口成功:");
        logger.debug(encryptionCardDO.toString());
        return response;
    }

}
